﻿using Furion.CMS.Core;
using Furion.CMS.Core.EventSource;
using Furion.CMS.Core.Handlers;
using Furion.CMS.EntityFramework.Core;
using Furion.DatabaseAccessor;
using Furion.DependencyInjection;
using Furion.EventBus;
using Furion.TaskScheduler;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Furion.CMS.Domain
{
    public class OrganizationFormatHandler : AuditLogFormatHandler
    {
        public override string[] GetChangeValue(DbContext context, object oldValue, object newValue)
        {
            Guid.TryParse(oldValue?.ToString(), out var oldId);
            Guid.TryParse(newValue?.ToString(), out var newId);
            var oldOrg = context.Find<OrganizationInfo>(oldId);
            var newOrg = context.Find<OrganizationInfo>(newId);

            return new string[2] { oldOrg?.Name ?? "无", newOrg?.Name ?? "无" };
        }
    }

    /// <summary>
    /// 机构信息
    /// </summary>
    [OperateRecord("组织机构管理")]
    public class OrganizationInfo : IEntity, IEntityTypeBuilder<OrganizationInfo>,
        IEntityChangedListener<OrganizationInfo>
    {
        public OrganizationInfo()
        {
            this.CreatedTime = DateTimeOffset.UtcNow;
            this.Users = new HashSet<UserInfo>();
            this.Roles = new HashSet<RoleInfo>();
            this.Children = new HashSet<OrganizationInfo>();
        }

        /// <summary>
        /// 机构Id
        /// </summary>
        public Guid Id { get; set; }
        /// <summary>
        /// 机构名称
        /// </summary>
        [MaxLength(100)]
        [OperateRecord("机构名称", true)]
        public string Name { get; set; }
        /// <summary>
        /// 是否启用
        /// </summary>
        [OperateRecord("是否启用")]
        public bool Enable { get; set; }
        /// <summary>
        /// 排序Id
        /// </summary>
        public int OrderId { get; set; }
        /// <summary>
        /// 父节点Id
        /// </summary>
        [OperateRecord("上级机构", typeof(OrganizationFormatHandler))]
        public Guid? ParentId { get; set; }

        /// <summary>
        /// 节点路径
        /// </summary>
        [MaxLength(400)]
        public string Path { get; set; }
        /// <summary>
        /// 父对象
        /// </summary>
        public virtual OrganizationInfo Parent { get; set; }
        /// <summary>
        /// 租户Id
        /// </summary>
        public virtual Guid? TenantId { get; set; }
        /// <summary>
        /// 创建时间
        /// </summary>
        public DateTimeOffset CreatedTime { get; set; }
        /// <summary>
        /// 修改时间
        /// </summary>
        public DateTimeOffset? UpdatedTime { get; set; }
        /// <summary>
        /// 包含用户
        /// </summary>
        public virtual ICollection<UserInfo> Users { get; set; }
        /// <summary>
        /// 包含角色
        /// </summary>
        public virtual ICollection<RoleInfo> Roles { get; set; }
        /// <summary>
        /// 子集合
        /// </summary>
        public virtual ICollection<OrganizationInfo> Children { get; set; }

        public void Configure(EntityTypeBuilder<OrganizationInfo> entityBuilder, DbContext dbContext, Type dbContextLocator)
        {
            entityBuilder.UseDefaultTableNameDefine();

            // TODO:机构移除时需要排除对关联数据的影响,待验证
            // .OnDelete(DeleteBehavior.SetNull)
            entityBuilder.HasMany(m => m.Users)
                .WithMany(m => m.Organizations)
                .UsingEntity<OrganizationUser>(
                r => r.HasOne(x => x.User).WithMany().HasForeignKey(p => p.UserId),
                l => l.HasOne(x => x.Organization).WithMany().HasForeignKey(p => p.OrganizationId)
                );

            entityBuilder.HasMany(m => m.Roles)
                .WithMany(m => m.Organizations)
                .UsingEntity<OrganizationRole>(
                r => r.HasOne(x => x.Role).WithMany().HasForeignKey(p => p.RoleId),
                l => l.HasOne(x => x.Organization).WithMany().HasForeignKey(p => p.OrganizationId)
                );

            // 递归外键,必须启用查询跟踪才能正常使用
            entityBuilder.HasMany(p => p.Children)
                .WithOne(p => p.Parent)
                .HasForeignKey(f => f.ParentId)
                .OnDelete(DeleteBehavior.ClientSetNull)
                .IsRequired(false);
        }

        public void OnChanging(OrganizationInfo entity, DbContext dbContext, Type dbContextLocator, EntityState state)
        {
            if (state == EntityState.Deleted)
            {
                dbContext.Entry(entity).Collection(c => c.Users).Load();
                dbContext.Entry(entity).Collection(c => c.Roles)
                    .Query()
                    .Include(i => i.Permissions)
                    .Load();
            }
        }

        public void OnChanged(OrganizationInfo newEntity, OrganizationInfo oldEntity, DbContext dbContext, Type dbContextLocator, EntityState state)
        {
            // TODO:删除机构时需要动态更新缓存并发起通知
            if (state == EntityState.Detached)
            {
                SpareTime.DoIt(() =>
                {
                    // 当前机构存在角色与人员时将进行缓存更新处理
                    if (newEntity.Roles.Any() && newEntity.Users.Any())
                    {
                        var dependentResList = newEntity.Roles.SelectMany(s => s.Permissions.Where(t => !string.IsNullOrWhiteSpace(t.DependentResources)).SelectMany(a => a.DependentResources.Split(new String[] { "," }, StringSplitOptions.RemoveEmptyEntries)));
                        var resourcePayload = newEntity.Users.Select(s => string.Format(CacheKeyConsts.PermissionResourceValue, newEntity.Id, s.Id));
                        // 获取资源Id与限制列表
                        var changeEvents = dependentResList.Select(s => new OnPermissionDataItemChange(CacheOperationType.Remove, string.Format(CacheKeyConsts.PermissionResource, s, ""), resourcePayload));

                        // 触发事件
                        var eventPublisher = App.GetService<IEventPublisher>();
                        eventPublisher?.PublishAsync(new ChannelEventSource(EventIdConsts.PermissionDataItemManyChange, changeEvents));
                    }
                });
            }
        }
    }
}
